import { useDebounceFn, useThrottleFn } from '@vueuse/core';
import type { Ref } from 'vue';
import { ref, unref, watch } from 'vue';

export type RemoveEventFn = () => void;

type TargetRef = Element | Ref<Element | undefined> | Window | any;

interface ExtraOption {
  wait?: number;
  isDebounce?: boolean;
  autoRemove?: boolean;
}

/**
 * @description 页面事件监听
 * @author wfd
 * @date 2022/11/3 08:51
 * @example useEventListener(window, 'drag', () => {
 *            show.value = false;
 *          }, {
 *            capture: true
 *          });
 * @param {TargetRef} el
 * @param {string} name
 * @param {(e: any) => void} listener
 * @param {boolean | AddEventListenerOptions} options
 * @param {ExtraOption} extraOption
 * @returns {{removeEvent: RemoveEventFn}}
 */
export function useEventListener (
  el: TargetRef, name: string,
  listener: (e: any) => void,
  options?: boolean | AddEventListenerOptions,
  extraOption?: ExtraOption): {removeEvent: RemoveEventFn} {
  
  let remove: RemoveEventFn = () => {
  };
  const isAddRef = ref(false);
  const { wait = 0, isDebounce = true, autoRemove = true } = extraOption || {};
  
  if (el) {
    const element = ref(el as Element) as Ref<Element>;
    
    const handler = isDebounce ? useDebounceFn(listener, wait) : useThrottleFn(listener, wait);
    const realHandler = wait ? handler : listener;
    const removeEventListener = (e: Element) => {
      isAddRef.value = true;
      e.removeEventListener(name, realHandler, options);
    };
    const addEventListener = (e: Element) => e.addEventListener(name, realHandler, options);
    
    const removeWatch = watch(
      element,
      (v, _ov, cleanUp) => {
        if (v) {
          !unref(isAddRef) && addEventListener(v);
          cleanUp(() => {
            autoRemove && removeEventListener(v);
          });
        }
      },
      { immediate: true }
    );
    
    remove = () => {
      removeEventListener(element.value);
      removeWatch();
    };
  }
  return { removeEvent: remove };
}
